package emailTransfer;

import fileIO.ReadTxtFiles;
import guiParts.EmailSendGui;
import guiParts.ErrorGui;
import guiParts.PasswordGui;

import java.awt.Toolkit;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.io.IOException;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import java.util.Set;

import javax.activation.DataHandler;
import javax.activation.FileDataSource;
import javax.mail.AuthenticationFailedException;
import javax.mail.Authenticator;
import javax.mail.Message;
import javax.mail.NoSuchProviderException;
import javax.mail.PasswordAuthentication;
import javax.mail.SendFailedException;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import javax.swing.SwingWorker;

import panelSites.SendingPanel;
import persistence.Serializer;
import propertiesReader.PropertiesReader;

public class SendEmail extends SwingWorker<Boolean, Void> implements Runnable,
		PropertyChangeListener {

	private boolean endproductrdy;

	// Log vars
	private boolean wrongPassword = false;
	private boolean emailsWrong = false;
	private boolean generalWrong = false;

	// Thread stopVar
	private boolean run = false;

	// JProgressBar
	private int sizeOfemails;
	private int actualEmailNr;

	// Message Parts
	private String subject;
	private String attachmentPath;

	// Store send emails
	HashSet<String> sendedEmails;

	// Technical Parameter
	// Variablen
	private String username;
	private String host;
	private String port;
	private String senderAddress;
	private String text;
	private File file2send;
	private Set<String> recipientsAddress;
	private Set<String> wrongAddress = new HashSet<String>();;
	private List<String> blackList;

	ReadTxtFiles txtReader = new ReadTxtFiles();

	private Session session;

	/**
	 * Singleton-Pattern
	 * 
	 * @param end
	 *            - is it an endversion, a jar?
	 * @param subject
	 *            - subject of the emails
	 * @param content
	 *            - content of the emails
	 * @param attachmentPath
	 *            - att... blabla
	 * @param xlsPath
	 *            - Path of the xls-file with the stored emailadresse in
	 */

	private static SendEmail sendEmail;
	private SendingPanel sendingPanel;
	private EmailSendGui view;

	private String password;

	private PropertiesReader pReader;

	private boolean htmlChecked;
	private boolean attach;

	private SendEmail(EmailSendGui view, SendingPanel sendingPanel,
			boolean end, String subject, String content, String attachmentPath,
			Set<String> set, boolean typeHtml, boolean attach)
			throws IOException {

		this.view = view;
		pReader = new PropertiesReader(end);

		this.htmlChecked = typeHtml;
		this.attach = attach;
		this.subject = subject;
		this.attachmentPath = attachmentPath;
		this.recipientsAddress = set;
		setEndproductrdy(end);
		this.sendedEmails = new HashSet<String>();
		this.sendingPanel = sendingPanel;
		this.senderAddress = pReader.getProperty("senderAddress");
		this.file2send = new File(this.attachmentPath);
		// Variablen
		this.username = getProperty("username");
		this.host = getProperty("mail.smtp.host");
		this.port = getProperty("mail.smtp.port");
		this.text = getTextFile(getProperty("newsTextPath"));
		// text = TextToHtml.convertText(text);

		blackList = txtReader
				.readTxtFileReturnLines(getProperty("blacklistPath"));

		set.removeAll(blackList);

		for (String string : blackList) {
			System.out.println("Blacklisting this adress: " + string);

		}

		wrongAddress.addAll(blackList);

		password = getPassword();

		session = prepareTechnicalPart(password, username, host, port);

		addPropertyChangeListener(this);

	}

	public static SendEmail getInstance(EmailSendGui view,
			SendingPanel sendingPanel, boolean end, String subject,
			String content, String attachmentPath,
			Set<String> set, boolean typeHtml, boolean attach)
			throws IOException {
		if (sendEmail == null) {
			return new SendEmail(view, sendingPanel, end, subject, content,
					attachmentPath, set, typeHtml, attach);
		} else {
			// set attributes
			sendEmail.setEndproductrdy(end);
			sendEmail.htmlChecked = typeHtml;
			sendEmail.attach = attach;
			return sendEmail;
		}
	}

	public void serializeCorrect(Set<String> sendedEmails) {
		Serializer serializer = new Serializer();
		serializer.serialize(pReader.getProperty("serializedEmailsPath"),
				sendedEmails);
	}

	public void serializeWrong(Set<String> wrongEmails) {
		Serializer serializer = new Serializer();
		serializer.serialize(pReader.getProperty("serializedWrongEmails"),
				wrongEmails);
	}

	/*
	 * Executed in event dispatching thread
	 */
	@Override
	public void done() {
		Toolkit.getDefaultToolkit().beep();
		serializeCorrect(sendedEmails);
		serializeWrong(wrongAddress);
		view.getStop().setEnabled(false);
		view.getSendingStatus().setEnabled(false);
		view.getSendingStatus().setVisible(false);
		view.getSend().setEnabled(true);
	}

	/*
	 * Main task. Executed in background thread.
	 */
	@Override
	public Boolean doInBackground() {
		try {
			int progress = 0;
			// Initialize progress property.
			setProgress(0);

			String encoding = "utf-8";

			// new mimemessage
			MimeMessage msg = new MimeMessage(session);

			// set recipients
			msg.setFrom(new InternetAddress(senderAddress));

			// prepare a MimeBodyPart for the content/text
			MimeBodyPart textBodyPart = new MimeBodyPart();

			if (htmlChecked) {
				textBodyPart.setContent(text, "text/html");
			} else {
				textBodyPart.setText(text, encoding);
			}
			textBodyPart.setHeader("MIME-Version", "1.0");
			// textBodyPart.setHeader("Content-Type",
			// textBodyPart.getContentType());
			textBodyPart.setDisposition(MimeBodyPart.INLINE);

			msg.setSubject(subject, encoding);

			// Attachment
			if (!file2send.exists()) {
				new ErrorGui("Fehler:	Anhang \n" + file2send
						+ "\n konnte nicht gefunden werden.");
				return false;
			}
			MimeBodyPart attachement = null;
			if (attach) {
				attachement = new MimeBodyPart();
				attachement.setDataHandler(new DataHandler(new FileDataSource(
						file2send)));
				attachement.setFileName(file2send.getName());
				attachement.setDisposition(MimeBodyPart.ATTACHMENT);
			}

			// add parts to main-message
			MimeMultipart mimeMultipart = new MimeMultipart();
			mimeMultipart.addBodyPart(textBodyPart);

			if (attach) {
				mimeMultipart.addBodyPart(attachement);
			}

			msg.setContent(mimeMultipart);

			// Header
			msg.setSentDate(new Date());
			// set Type and Charset in Headerfield 'Content-Type'
			msg.setHeader("Content-Transfer-Encoding", encoding);
			// msg.addHeader("Content-Type", "text/plain; charset=ISO-8859-1");
			// msg.saveChanges();

			// at last : send the mail
			long start = 0, end;
			double rdy = 0;

			// clear mails
			for (Iterator<String> iter = recipientsAddress.iterator(); iter
					.hasNext();) {
				String email = iter.next();
				if (!validateEmail(email)) {
					iter.remove();
				}
			}

			// for each recipient
			Iterator<String> adresses = recipientsAddress.iterator();
			while(adresses.hasNext()){
				String recipientsAddress = adresses.next();

				if (run) {
					System.out.println("send mail to " + recipientsAddress);

					try {
						start = System.currentTimeMillis();
						msg.setRecipients(Message.RecipientType.TO,
								InternetAddress.parse(recipientsAddress, false));
						Transport transport = session.getTransport("smtp");
						transport.connect(username, password);
						Transport.send(msg);
						transport.close();
						sendedEmails.add(recipientsAddress);
					} catch (SendFailedException e) {
						e.printStackTrace();
						// @SuppressWarnings("unused")
						// ErrorGui errorGui = new ErrorGui(
						// "Fehler:	Email an "
						// + recipientsAddress
						// + " konnte nicht gesendet werden. Adresse korrekt?");
						wrongAddress.add(recipientsAddress);
					} catch (NoSuchProviderException e) {
						e.printStackTrace();
						// @SuppressWarnings("unused")
						// ErrorGui errorGui = new ErrorGui(
						// "Fehler:	Angegebener Provider nicht erreichbar.");
					}
					rdy++;
					try {
						Thread.sleep(800);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					System.out.println("rdy: " + rdy);
					System.out.println("this.recipientsAddress.size(): "
							+ this.recipientsAddress.size());
					progress = (int) ((rdy / this.recipientsAddress.size()) * 100);
					System.out.println("progress percent: " + progress);
					// progress += done;
					setProgress(Math.min(progress, 100));

					end = System.currentTimeMillis();
					System.out.println((end - start) / 1000.0 + " sec");
				} else {
					System.out.println("System stopped.");
					wrongPassword = false;
					generalWrong = false;
					return false;
				}
			}

		} catch (AuthenticationFailedException e) {
			e.printStackTrace();
			setWrongPassword(true);
			String errMsg = e.getMessage();
			new ErrorGui("Fehler: " + errMsg);
			generalWrong = true;
			sendingPanel.progressBar.setValue(0);
			setProgress(0);
			return false;
		} catch (Exception e) {
			System.err
					.println("Malfunction of sending process. System will exit.");
			e.printStackTrace();
			setGeneralWrong(true);
			return false;
		}
		return true;
	}

	private boolean validateEmail(String email) {
		try {
			new InternetAddress(email).validate();

		} catch (AddressException ex) {
			System.out.println("Error : " + ex.getMessage());
			return false;
		}
		return true;
	}

	public void propertyChange(PropertyChangeEvent evt) {
		if ("progress" == evt.getPropertyName()) {
			int progress = (Integer) evt.getNewValue();
			sendingPanel.progressBar.setValue(progress);
		}
	}

	public HashSet<String> loadAndReturnSendedEmailList() {
		Serializer serializer = new Serializer();
		Set<String> sendedEmailsLoad = serializer.deserialize(pReader
				.getProperty("serializedEmailsPath"));
		if (sendedEmailsLoad != null)
			sendedEmails.addAll(sendedEmailsLoad);
		return sendedEmails;
	}

	private String getProperty(String key) {
		System.out.print(key + " : ");
		String value = pReader.getProperty(key);
		System.out.println(value);
		return value;
	}

	private String getTextFile(String source) throws IOException {
		return txtReader.readTxtFile(source);

	}

	private static String getPassword() {
		PasswordGui pwInput = new PasswordGui();
		return pwInput.readPassword();
	}

	private Session prepareTechnicalPart(String password, String username,
			String host, String port) {
		MailAuthenticator auth = new MailAuthenticator(username, password);

		Properties properties = new Properties();
		// properties.setProperty("mail.smtp.submitter",
		// auth.getPasswordAuthentication().getUserName());
		properties.setProperty("mail.smtp.auth", "true");
		properties.setProperty("mail.smtp.user", username);
		properties.setProperty("mail.smtp.host", host);
		properties.setProperty("mail.smtp.port", port);
		properties.setProperty("mail.mime.charset", "utf-8");

		Session session = Session.getInstance(properties, auth);
		// session.setDebug(true);
		return session;
	}

	@SuppressWarnings("unused")
	private void printInfos() {
		System.out.println("subject " + subject);
		System.out.println("senderad " + senderAddress);
		System.out.println("subject " + subject);
		System.out.println("subject " + subject);
	}

	class MailAuthenticator extends Authenticator {

		private PasswordAuthentication passwordAuth;

		public MailAuthenticator(String user, String password) {
			passwordAuth = new PasswordAuthentication(user, password);
		}

		protected PasswordAuthentication getPasswordAuthentication() {
			return passwordAuth;
		}
	}

	public void resetListWithSendedEmails() {
		sendedEmails.clear();
		serializeCorrect(sendedEmails);
	}

	public boolean isWrongPassword() {
		return wrongPassword;
	}

	public void setWrongPassword(boolean wrongPassword) {
		this.wrongPassword = wrongPassword;
	}

	public boolean isEmailsWrong() {
		return emailsWrong;
	}

	public void setEmailsWrong(boolean emailsWrong) {
		this.emailsWrong = emailsWrong;
	}

	public void setGeneralWrong(boolean generalWrong) {
		this.generalWrong = generalWrong;
	}

	public boolean isGeneralWrong() {
		return generalWrong;
	}

	public void setSizeOfemails(int sizeOfemails) {
		this.sizeOfemails = sizeOfemails;
	}

	public int getSizeOfemails() {
		return sizeOfemails;
	}

	public void setActualEmailNr(int actualEmailNr) {
		this.actualEmailNr = actualEmailNr;
	}

	public int getActualEmailNr() {
		return actualEmailNr;
	}

	public boolean isRun() {
		return run;
	}

	public void setRun(boolean run) {
		System.out.println("run changed");
		this.run = run;

	}

	public boolean isEndproductrdy() {
		return endproductrdy;
	}

	public void setEndproductrdy(boolean endproductrdy) {
		this.endproductrdy = endproductrdy;
	}

	public void serialize() {
		serializeCorrect(sendedEmails);
		serializeWrong(wrongAddress);

	}

}
